home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / program / tdk_v120.zip / EXEC.PAS < prev    next >
Pascal/Delphi Source File  |  1996-07-15  |  37KB  |  1,193 lines

  1. {
  2.  ▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀    ▀▀   ▀▀
  3.    ▀▀     ▀▀   ▀▀   ▀▀  ▀▀
  4.   ▀▀     ▀▀   ▀▀▀  ▀▀▀▀▀  The DoorKit!
  5.  ▀▀     ▀▀   ▀▀   ▀▀  ▀▀
  6. ▀▀     ▀▀▀▀▀▀    ▀▀    ▀▀
  7. The BBS Door Development Kit By The People - For The People!
  8.  
  9.  
  10.    Feel free to modify or optimize this code at will. All I ask is that if
  11.    find a better way to do things (and you will), please send me a copy of
  12.    your modifications. Thanks in advance!....Larry L. Athey....}
  13.  
  14. Unit EXEC;
  15. {  --- Version 3.3 93-06-22 14:45 ---
  16.  
  17.    EXEC.PAS: EXEC function with memory swap - prepare parameters.
  18.  
  19.    Needs Assembler file 'spawn.asm' (assembled as 'spawnp.obj')
  20.    and unit 'checkpat'.
  21.  
  22. Public domain software by
  23.  
  24.         Thomas Wagner
  25.         Ferrari electronic GmbH
  26.         Beusselstrasse 27
  27.         D-1000 Berlin 21
  28.         West Germany
  29.  
  30.         BIXname: twagner
  31. }
  32.  
  33. INTERFACE
  34.  
  35. Uses
  36.   DOS, checkpat;
  37.  
  38. CONST
  39.  
  40. {e Return codes (only upper byte significant) }
  41. {d Fehlercodes (nur das obere Byte signifikant) }
  42.  
  43.    RC_PREPERR   = $0100;
  44.    RC_NOFILE    = $0200;
  45.    RC_EXECERR   = $0300;
  46.    RC_ENVERR    = $0400;
  47.    RC_SWAPERR   = $0500;
  48.    RC_REDIRERR  = $0600;
  49.  
  50. {e Swap method and option flags }
  51. {d Auslagerungsmethoden ond Optionen }
  52.  
  53.    USE_EMS      =  $01;
  54.    USE_XMS      =  $02;
  55.    USE_FILE     =  $04;
  56.    EMS_FIRST    =  $00;
  57.    XMS_FIRST    =  $10;
  58.    HIDE_FILE    =  $40;
  59.    NO_PREALLOC  = $100;
  60.    CHECK_NET    = $200;
  61.  
  62.    USE_ALL      = USE_EMS OR USE_XMS OR USE_FILE OR CHECK_NET;
  63.  
  64.  
  65. TYPE
  66.     filename = STRING [81];
  67.     string128 = STRING [128];
  68.     pstring = ^STRING;
  69.  
  70.  
  71. FUNCTION do_exec (xfn : STRING; pars : STRING; spawn : INTEGER;
  72.                   needed : WORD; newenv : BOOLEAN) : INTEGER;
  73.  
  74.    {>e
  75.       The EXEC function.
  76.  
  77.       Parameters:
  78.  
  79.          xfn      is a string containing the name of the file
  80.                   to be executed. If the string is empty,
  81.                   the COMSPEC environment variable is used to
  82.                   load a copy of COMMAND.COM or its equivalent.
  83.                   If the filename does not include a path, the
  84.                   current PATH is searched after the default.
  85.                   If the filename does not include an extension,
  86.                   the path is scanned for a COM, EXE, or BAT file 
  87.                   in that order.
  88.  
  89.          pars     The program parameters.
  90.  
  91.          spawn    If 0, the function will terminate after the 
  92.                   EXECed program returns, the function will not return.
  93.  
  94.                   NOTE: If the program file is not found, the function
  95.                         will always return with the appropriate error 
  96.                         code, even if 'spawn' is 0.
  97.  
  98.                   If non-0, the function will return after executing the
  99.                   program. If necessary (see the "needed" parameter),
  100.                   memory will be swapped out before executing the program.
  101.                   For swapping, spawn must contain a combination of the
  102.                   following flags:
  103.  
  104.                      USE_EMS  ($01)  - allow EMS swap
  105.                      USE_XMS  ($02)  - allow XMS swap
  106.                      USE_FILE ($04)  - allow File swap
  107.  
  108.                   The order of trying the different swap methods can be
  109.                   controlled with one of the flags
  110.  
  111.                      EMS_FIRST ($00) - EMS, XMS, File (default)
  112.                      XMS_FIRST ($10) - XMS, EMS, File
  113.  
  114.                   If swapping is to File, the attribute of the swap file
  115.                   can be set to "hidden", so users are not irritated by
  116.                   strange files appearing out of nowhere with the flag
  117.  
  118.                      HIDE_FILE ($40) - create swap file as hidden
  119.  
  120.                   and the behaviour on Network drives can be changed with
  121.  
  122.                      NO_PREALLOC (0x100) - don't preallocate
  123.                      CHECK_NET (0x200)   - don't preallocate if file on net.
  124.  
  125.                   This checking for Network is mainly to compensate for
  126.                   a strange slowdown on Novell networks when preallocating
  127.                   a file. You can either set NO_PREALLOC to avoid allocation
  128.                   in any case, or let the prep_swap routine decide whether
  129.                   to do preallocation or not depending on the file being
  130.                   on a network drive (this will only work with DOS 3.1 or 
  131.                   later).
  132.  
  133.          needed   The memory needed for the program in paragraphs (16 Bytes).
  134.                   If not enough memory is free, the program will
  135.                   be swapped out.
  136.                   Use 0 to never swap, $ffff to always swap. 
  137.                   If 'spawn' is 0, this parameter is irrelevant.
  138.  
  139.          newenv   If this parameter is FALSE, the environment
  140.                   of the spawned program is a copy of the parent's
  141.                   environment. If it is TRUE, a new environment
  142.                   is created which includes the modifications from
  143.                   previous 'putenv' calls.
  144.  
  145.       Return value:
  146.  
  147.          $0000..00FF: The EXECed Program's return code
  148.  
  149.          $0101:       Error preparing for swap: no space for swapping
  150.          $0102:       Error preparing for swap: program too low in memory
  151.  
  152.          $0200:       Program file not found
  153.          $0201:       Program file: Invalid drive
  154.          $0202:       Program file: Invalid path
  155.          $0203:       Program file: Invalid name
  156.          $0204:       Program file: Invalid drive letter
  157.          $0205:       Program file: Path too long
  158.          $0206:       Program file: Drive not ready
  159.          $0207:       Batchfile/COMMAND: COMMAND.COM not found
  160.          $0208:       Error allocating temporary buffer
  161.  
  162.          $03xx:       DOS-error-code xx calling EXEC
  163.  
  164.          $0400:       Error allocating environment buffer
  165.  
  166.          $0500:       Swapping requested, but prep_swap has not 
  167.                        been called or returned an error.
  168.          $0501:       MCBs don't match expected setup
  169.          $0502:       Error while swapping out
  170.  
  171.          $0600:       Redirection syntax error
  172.          $06xx:       DOS error xx on redirection
  173.    <}
  174.  
  175.    {>d
  176.       Die EXEC Funktion.
  177.  
  178.       Parameter:
  179.  
  180.          xfn      ist ein String mit dem Namen der auszuführenden Datei.
  181.                   Ist der String leer, wird die COMSPEC Umgebungsvariable
  182.                   benutzt um COMMAND.COM oder das Equivalent zu laden.
  183.                   Ist kein Pfad angegeben, wird nach dem aktuellen Pfad
  184.                   der in der PATH Umgebungsvariablen angegebene Pfad
  185.                   durchsucht.
  186.                   Ist kein Dateityp angegeben, wird der Pfad nach
  187.                   einer COM oder EXE Datei (in dieser Reihenfolge) abgesucht.
  188.  
  189.          pars     Die Kommandozeile
  190.  
  191.          spawn    Wenn 0, wird der Programmlauf beendet wenn das
  192.                   aufgerufene Programm zurückkehrt, die Funktion kehrt
  193.                   nicht zurück.
  194.  
  195.                   HINWEIS: Wenn die auszuführende Datei nicht gefunden
  196.                         wird, kehrt die Funktion mit einem Fehlercode
  197.                         zurück, auch wenn der 'spawn' Parameter 0 ist.
  198.  
  199.                   Wenn nicht 0, kehrt die Funktion nach Ausführung des
  200.                   Programms zurück. Falls notwendig (siehe den Parameter
  201.                   "needed") wird der Programmspeicherbereich vor Aufruf
  202.                   ausgelagert.
  203.                   Zur Auslagerung muß der Parameter eine Kombination der
  204.                   folgenden Flags enthalten:
  205.  
  206.                      USE_EMS  ($01)  - Auslagerung auf EMS zulassen
  207.                      USE_XMS  ($02)  - Auslagerung auf XMS zulassen
  208.                      USE_FILE ($04)  - Auslagerung auf Datei zulassen
  209.  
  210.                   Die Reihenfolge der Versuche, auf die verschiedenen
  211.                   Medien auszulagern kann mit einem der folgenden
  212.                   Flags beeinflußt werden:
  213.  
  214.                      EMS_FIRST ($00) - EMS, XMS, Datei (Standard)
  215.                      XMS_FIRST ($10) - XMS, EMS, Datei
  216.  
  217.                   Wenn die Auslagerung auf Datei erfolgt, kann das
  218.                   Attribut dieser Datei auf "hidden" gesetzt werden,
  219.                   damit der Benutzer nicht durch unversehends auftauchende
  220.                   Dateien verwirrt wird:
  221.  
  222.                      HIDE_FILE ($40) - Auslagerungsdatei "hidden" erzeugen
  223.  
  224.                   Außerdem kann das Verhalten auf Netzwerk-Laufwerken 
  225.                   beeinflußt werden mit
  226.  
  227.                      NO_PREALLOC (0x100) - nicht Präallozieren
  228.                      CHECK_NET (0x200)   - nicht Präallozieren wenn Netz.
  229.  
  230.                   Diese Prüfung auf Netzwerk ist hauptsächlich sinnvoll
  231.                   für Novell Netze, bei denen eine Präallozierung eine
  232.                   erhebliche Verzögerung bewirkt. Sie können entweder mit
  233.                   NO_PREALLOC eine Präallozierung in jedem Fall ausschließen,
  234.                   oder die Entscheidung mit CHECK_NET prep_swap überlassen.
  235.                   In diesem Fall wird nicht präalloziert wenn die Datei
  236.                   auf einem Netzwerk-Laufwerk liegt (funktioniert nur
  237.                   mit DOS Version 3.1 und späteren).
  238.  
  239.          needed   Der zur Ausführung des Programms benötigte Speicher
  240.                   in Paragraphen (16 Bytes). Wenn nicht ausreichend 
  241.                   freier Speicher vorhanden ist, wird der Programm-
  242.                   speicherbereich ausgelagert.
  243.                   Bei Angabe von 0 wird nie ausgelagert, bei Angabe
  244.                   von $ffff wird immer ausgelagert.
  245.                   Ist der Parameter 'spawn' 0, hat 'needed' keine Bedeutung.
  246.  
  247.          newenv   Bestimmt die dem gerufenen Programm zu übergebenden 
  248.                   Umgebungsvariablen. Ist der Parameter FALSE,
  249.                   wird eine Kopie der Vater-Umgebung benutzt,
  250.                   d.h. daß Aufrufe von "putenv" keinen Effekt haben.
  251.                   Ist er TRUE, wird eine neue Umgebung mit den 
  252.                   Modifikationen aus 'putenv' übergeben.
  253.  
  254.       Liefert:
  255.  
  256.          $0000..00FF: Rückgabewert des aufgerufenen Programms
  257.  
  258.          $0101:       Fehler bei Vorbereitung zum Auslagern -
  259.                        kein Speicherplatz in XMS/EMS/Datei
  260.          $0102:       Fehler bei Vorbereitung zum Auslagern -
  261.                        der Programmcode ist zu nah am Beginn des
  262.                        Programms.
  263.  
  264.          $0200:       Auszuführende Programmdatei nicht gefunden
  265.          $0201:       Programmdatei: Ungültiges Laufwerk
  266.          $0202:       Programmdatei: Ungültiger Pfad
  267.          $0203:       Programmdatei: Ungültiger Dateiname
  268.          $0204:       Programmdatei: Ungültiger Laufwerksbuchstabe
  269.          $0205:       Programmdatei: Pfad zu lang
  270.          $0206:       Programmdatei: Laufwerk nicht bereit
  271.          $0207:       Batchfile/COMMAND: COMMAND.COM nicht gefunden
  272.          $0208:       Fehler beim allozieren eines temporären Puffers
  273.  
  274.          $03xx:       DOS-Fehler-Code xx bei Aufruf von EXEC
  275.  
  276.          $0400:       Fehler beim allozieren der Umgebungsvariablenkopie
  277.  
  278.          $0500:       Auslagerung angefordert, aber prep_swap wurde nicht
  279.                        aufgerufen oder lieferte einen Fehler
  280.          $0501:       MCBs entsprechen nicht dem erwarteten Aufbau
  281.          $0502:       Fehler beim Auslagern
  282.  
  283.          $0600:      Redirection Syntaxfehler
  284.          $06xx:      DOS-Fehler xx bei Redirection
  285.    <}
  286.  
  287. {>e
  288.    The function pointed to by "spawn_check" will be called immediately 
  289.    before doing the actual swap/exec, provided that
  290.  
  291.       - the preparation code did not detect an error, and
  292.       - "spawn_check" is not NIL.
  293.  
  294.    The function definition is
  295.       function name (cmdbat: integer; swapping: integer; var execfn: string; 
  296.                      var progpars: string): integer;
  297.  
  298.    The parameters passed to this function are
  299.  
  300.       cmdbat      1: Normal EXE/COM file
  301.                   2: Executing BAT file via COMMAND.COM
  302.                   3: Executing COMMAND.COM (or equivalent)
  303.  
  304.       swapping    < 0: Exec, don't swap
  305.                     0: Spawn, don't swap
  306.                   > 0: Spawn, swap
  307.  
  308.       execfn      the file name to execute (complete with path)
  309.  
  310.       progpars    the program parameter string
  311.  
  312.    If the routine returns anything other than 0, the swap/exec will
  313.    not be executed, and do_exec will return with this code.
  314.  
  315.    You can use this function to output messages (for example, the
  316.    usual "enter EXIT to return" message when loading COMMAND.COM)
  317.    and to do clean-up and additional checking.
  318.  
  319.    CAUTION: If swapping is > 0, the routine may not modify the 
  320.    memory layout, i.e. it may not call any memory allocation or
  321.    deallocation routines.
  322.  
  323.    "spawn_check" is initialized to NIL.
  324. <}
  325. {>d
  326.    Die Funktion auf die "spawn_check" zeigt wird unmittelbar vor
  327.    Ausführung des Programmaufrufs aufgerufen, vorausgesetzt daß
  328.  
  329.       - bei der Vorbereitung kein Fehler auftrat, und
  330.       - "spawn_check" nicht NIL ist.
  331.  
  332.    Die Funktionsdefinition ist
  333.       function name (cmdbat: integer; swapping: integer; var execfn: string; 
  334.                      var progpars: string): integer;
  335.  
  336.    Die der Funktion übergebenen Parameter sind
  337.  
  338.       cmdbat      1: Normale EXE/COM Datei
  339.                   2: Ausführung BAT Datei über COMMAND.COM
  340.                   3: Ausführung COMMAND.COM (oder Equivalent)
  341.  
  342.       swapping    < 0: Exec, keine Auslagerung
  343.                     0: Spawn, keine Auslagerung
  344.                   > 0: Spawn, Auslagern
  345.  
  346.       execfn      Name und Pfad der auszuführenden Datei
  347.  
  348.       progpars    Programmparameter
  349.  
  350.    Wenn die Routine einen Wert verschieden von 0 liefert, wird der
  351.    Programmaufruf nicht durchgeführt, und do_exec kehrt mit diesem
  352.    Wert zurück.
  353.  
  354.    Sie können diese Funktion benutzen um Meldungen auszugeben
  355.    (zum Beispiel die übliche Meldung "Geben Sie EXIT ein um 
  356.    zurückzukehren" bei Laden von COMMAND.COM), und für sonstige
  357.    Prüfungen oder Aufräumarbeiten.
  358.  
  359.    ACHTUNG: Wenn swapping > 0 ist, darf die Funktion keinesfalls 
  360.    den Speicheraufbau verändern, d.h. es dürfen keine Speicher-
  361.    Allozierungs oder -Deallozierungsroutinen benutzt werden.
  362.  
  363.    "spawn_check" ist auf NIL initialisiert.
  364. <}
  365.  
  366. TYPE
  367.    spawn_check_proc = FUNCTION (cmdbat : INTEGER; swapping : INTEGER; 
  368.                                 VAR execfn : STRING; VAR progpars : STRING)
  369.                                : INTEGER;
  370. VAR
  371.    spawn_check : spawn_check_proc;
  372.  
  373. {>e
  374.    The 'swap_prep' variable can be accessed from the spawn_check
  375.    call-back routine for additional information on the nature and
  376.    parameters of the swap. This variable will ONLY hold useful
  377.    information if the 'swapping' parameter to spawn_check is > 0.
  378.    The contents of this variable may not be changed.
  379.  
  380.    The 'swapmethod' field will contain one of the flags USE_FILE, 
  381.    USE_XMS, or USE_EMS.
  382.  
  383.    Note that the 'swapfilename' field contains a zero-terminated string
  384.    with no prefixed length byte, not a Pascal string.
  385. <}
  386. {>d
  387.    Die Variable 'swap_prep' kann von der spawn_check Routine
  388.    benutzt werden um zusätzliche Informationen über Art und Parameter
  389.    der Auslagerung zu erfahren. Diese Variable enthält NUR DANN 
  390.    sinnvolle Werte wenn der 'swapping' Parameter von spawn_check > 0 ist.
  391.    Der Inhalt dieser Variablen darf keinesfalls verändert werden.
  392.  
  393.    Das Feld 'swapmethod' enthält einen der Werte USE_FILE, 
  394.    USE_XMS, oder USE_EMS.
  395.  
  396.    Bitte beachten Sie, daß das Feld 'swapfilename' einen Null-terminierten
  397.    String ohne Längenbyte, keinen Pascal-String, enthält.
  398. <}
  399.  
  400. TYPE
  401.    prep_block = RECORD
  402.                   xmm : LONGINT;           {e XMM entry address }
  403.                                           {d Einsprungadresse XMM }
  404.                   first_mcb : INTEGER;     {e Segment of first MCB }
  405.                                           {d Segment des ersten MCB }
  406.                   psp_mcb : INTEGER;       {e Segment of MCB of our PSP }
  407.                                           {d Segment des MCB unseres PSP }
  408.                   env_mcb : INTEGER;       {e MCB of Environment segment }
  409.                                           {d MCB des Umgebungsvariablenblocks }
  410.                   noswap_mcb : INTEGER;    {e MCB that may not be swapped }
  411.                                           {d MCB der nicht Ausgelagert wird }
  412.                   ems_pageframe : INTEGER; {e EMS page frame address }
  413.                                           {d EMS-Seiten-Adresse }
  414.                   handle : INTEGER;        {e EMS/XMS/File handle }
  415.                                           {d Handle für EMS/XMS/Datei }
  416.                   total_mcbs : INTEGER;    {e Total number of MCBs }
  417.                                           {d Gesamtzahl MCBs }
  418.                   swapmethod : BYTE;       {e Method for swapping }
  419.                                           {d Auslagerungsmethode }
  420.                   swapfilename : ARRAY [0..80] OF CHAR; 
  421.                                           {e Swap file name if swapping to file }
  422.                                           {d Auslagerungsdateiname }
  423.                   END;
  424.  
  425. VAR
  426.    swap_prep : prep_block;
  427.  
  428. { ------------------------------------------------------------------------- }
  429.  
  430. PROCEDURE putenv (envvar : STRING);
  431. {  Adds a string to the environment. Note that the change to the
  432.    environment is valid for an exec'ed process only, and only if you
  433.    set the 'newenv' parameter in do_exec to TRUE. }
  434.  
  435.  
  436. FUNCTION envcount : INTEGER;
  437. FUNCTION envstr (index : INTEGER) : STRING;
  438. FUNCTION getenv (envvar : STRING) : STRING;
  439.  
  440. { Replacement functions for the environment handling functions in the
  441.   DOS unit. All three functions work exactly like their DOS-unit
  442.   counterparts, except that they recognize the changes to the child
  443.   environment produced by 'putenv'. }
  444.  
  445.  
  446.  
  447. {===========================================================================}
  448.  
  449. IMPLEMENTATION
  450.  
  451. {>e
  452.    Define REDIRECT to support redirection.
  453.    CAUTION: The definition in 'spawn.asm' must match this definition!!
  454. <}
  455. {>d
  456.    Definieren Sie REDIRECT um Dateiumleitung zu untertützen.
  457.    ACHTUNG: Die Definition in 'spawn.asm' muß mit dieser Definition 
  458.    übereinstimmen!!
  459. <}
  460.  
  461. {$DEFINE REDIRECT}
  462.  
  463. CONST
  464.    swap_filename = '$$AAAAAA.AAA';
  465.  
  466.    {e internal flags for prep_swap }
  467.    {d interne Flags für prep_swap }
  468.  
  469.    CREAT_TEMP      = $0080;
  470.    DONT_SWAP_ENV   = $4000;
  471.  
  472.    ERR_COMSPEC     = - 7;
  473.    ERR_NOMEM       = - 8;
  474.  
  475.    spaces : SET OF #9..' ' = [#9, ' '];
  476.  
  477. TYPE
  478.    stringptr = ^STRING;
  479.    stringarray = ARRAY [0..10000] OF stringptr;
  480.    stringarrptr = ^stringarray;
  481.    bytearray = ARRAY [0..30000] OF BYTE;
  482.    bytearrayptr = ^bytearray;
  483.  
  484. VAR
  485.    envptr : stringarrptr;   { Pointer to the changed environment }
  486.    envcnt : INTEGER;        { Count of environment strings }
  487.    cmdpath : STRING;
  488.    cmdpars : STRING;
  489.    drive : STRING [3];
  490.    dir : STRING [67];
  491.    name : STRING [9];
  492.    ext : STRING [5];
  493.  
  494.  
  495. {$L spawnp}
  496. FUNCTION do_spawn (swapping : INTEGER;
  497.                    VAR xeqfn; VAR cmdtail; envlen : WORD;
  498.                    VAR env
  499. {$IFDEF REDIRECT}
  500.                    ;stdin : pstring; stdout : pstring; stderr : pstring
  501. {$ENDIF}
  502.                    ) : INTEGER; EXTERNAL;
  503.  
  504. FUNCTION prep_swap (method : INTEGER; VAR swapfn) : INTEGER; EXTERNAL;
  505.  
  506.  
  507. { helper routine }
  508.  
  509. FUNCTION strpbrk (par, pattern : STRING) : INTEGER;
  510.    { find position of any one of the characters in 'pattern' in string 'par' }
  511.    VAR
  512.       i : INTEGER;
  513.    BEGIN
  514.    FOR i := 1 TO LENGTH (par) DO
  515.       IF POS (par [i], pattern) > 0
  516.          THEN BEGIN
  517.          strpbrk := i;
  518.          EXIT;
  519.          END;
  520.    strpbrk := 0;
  521.    END;
  522.      
  523. { Environment routines }
  524.  
  525. FUNCTION envcount : INTEGER;
  526.  
  527.    { Returns count of strings in environment. }
  528.  
  529.    VAR
  530.       cnt : INTEGER;
  531.    BEGIN
  532.    IF envptr = NIL { If not yet changed }
  533.       THEN envcount := DOS.envcount
  534.       ELSE envcount := envcnt;
  535.    END;
  536.  
  537.  
  538. FUNCTION envstr (index : INTEGER) : STRING;
  539.  
  540.    { Returns environment string 'index' }
  541.  
  542.    BEGIN
  543.    IF envptr = NIL { If not yet changed }
  544.       THEN envstr := DOS.envstr (index)
  545.       ELSE IF (index <= 0) OR (index >= envcnt)
  546.       THEN envstr := ''
  547.       ELSE IF envptr^ [index - 1] = NIL
  548.       THEN envstr := ''
  549.       ELSE envstr := envptr^ [index - 1]^;
  550.    END;
  551.  
  552.  
  553. FUNCTION name_eq (VAR n1, n2 : STRING) : BOOLEAN;
  554.  
  555.    { Compares search string 'n1' with environment string 'n2'.
  556.      Case is insignificant. }
  557.  
  558.    VAR
  559.       i : INTEGER;
  560.       eq : BOOLEAN;
  561.    BEGIN
  562.    i := 1;
  563.    eq := FALSE;
  564.    WHILE (i <= LENGTH (n1)) AND (i <= LENGTH (n2)) AND
  565.          (UPCASE (n1 [i]) = UPCASE (n2 [i])) DO
  566.       i := i + 1;
  567.    name_eq := (i > LENGTH (n1)) AND (i <= LENGTH (n2)) AND (n2 [i] = '=');
  568.    END;
  569.  
  570.  
  571. FUNCTION searchenv (VAR STR : STRING) : INTEGER;
  572.  
  573.    { Search for environment string, returns index in 'envptr' array.
  574.      Assumes 'envptr' is not NIL. }
  575.  
  576.    VAR
  577.       idx : INTEGER;
  578.       found : BOOLEAN;
  579.    BEGIN
  580.    idx := 0;
  581.    found := FALSE;
  582.  
  583.    WHILE (idx < envcnt) AND NOT found DO
  584.       BEGIN
  585.       IF envptr^ [idx] <> NIL
  586.          THEN found := name_eq (STR, envptr^ [idx]^);
  587.       idx := idx + 1;
  588.       END;
  589.    IF NOT found
  590.       THEN searchenv := - 1
  591.       ELSE searchenv := idx - 1;
  592.    END;
  593.  
  594.  
  595. FUNCTION getenv (envvar : STRING) : STRING;
  596.  
  597.    { Returns value of environment string specified by name. }
  598.  
  599.    VAR
  600.       strp : stringptr;
  601.       eq : INTEGER;
  602.    BEGIN
  603.    IF envptr = NIL { If not yet changed }
  604.       THEN getenv := DOS.getenv (envvar)
  605.       ELSE BEGIN
  606.       eq := searchenv (envvar);
  607.       IF eq < 0
  608.          THEN getenv := ''
  609.          ELSE BEGIN
  610.          strp := envptr^ [eq];
  611.          eq := POS ('=', strp^);
  612.          getenv := COPY (strp^, eq + 1, LENGTH (strp^) - eq);
  613.          END;
  614.       END;
  615.    END;
  616.  
  617.  
  618. PROCEDURE init_envptr;
  619.  
  620.    { Initialise 'envptr' array. Called when 'putenv' is used for the
  621.      first time. Copies all environment strings into heap storage,
  622.      and builds an array of pointers to this strings. }
  623.  
  624.    VAR
  625.       i : INTEGER;
  626.       STR : STRING [255];
  627.    BEGIN
  628.    envcnt := DOS.envcount;
  629.    GETMEM (envptr, envcnt * SIZEOF (stringptr));
  630.    IF envptr = NIL
  631.       THEN EXIT;
  632.    FOR i := 0 TO envcnt - 1 DO
  633.       BEGIN
  634.       STR := DOS.envstr (i + 1);
  635.       GETMEM (envptr^ [i], LENGTH (STR) + 1);
  636.       IF envptr^ [i] <> NIL
  637.          THEN envptr^ [i]^ := STR;
  638.       END;
  639.    END;
  640.  
  641.  
  642. PROCEDURE putenv (envvar : STRING);
  643.  
  644.    { Adds the string 'envvar' to the environment, or changes the
  645.      environment string if the name is already present. }
  646.  
  647.    VAR
  648.       idx, eq : INTEGER;
  649.       help : stringarrptr;
  650.       tmpvar : STRING;
  651.    BEGIN
  652.    IF envptr = NIL
  653.       THEN init_envptr;
  654.    IF envptr = NIL
  655.       THEN EXIT;
  656.  
  657.    eq := POS ('=', envvar);
  658.    IF eq = 0
  659.       THEN EXIT;
  660.    FOR idx := 1 TO eq DO
  661.       envvar [idx] := UPCASE (envvar [idx]);
  662.    tmpvar := COPY (envvar, 1, eq - 1); { Copy the portion up to "=" }
  663.  
  664.    idx := searchenv (tmpvar);
  665.    IF idx >= 0
  666.       THEN BEGIN
  667.       FREEMEM (envptr^ [idx], LENGTH (envptr^ [idx]^) + 1);
  668.  
  669.       IF eq >= LENGTH (envvar)
  670.          THEN envptr^ [idx] := NIL
  671.          ELSE BEGIN
  672.          GETMEM (envptr^ [idx], LENGTH (envvar) + 1);
  673.          IF envptr^ [idx] <> NIL
  674.             THEN envptr^ [idx]^ := envvar;
  675.          END;
  676.       END
  677.       ELSE IF eq < LENGTH (envvar)
  678.       THEN BEGIN
  679.       GETMEM (help, (envcnt + 1) * SIZEOF (stringptr));
  680.       IF help = NIL
  681.          THEN EXIT;
  682.       MOVE (envptr^, help^, envcnt * SIZEOF (stringptr));
  683.       FREEMEM (envptr, envcnt * SIZEOF (stringptr));
  684.       envptr := help;
  685.       GETMEM (envptr^ [envcnt], LENGTH (envvar) + 1);
  686.       IF envptr^ [envcnt] <> NIL
  687.          THEN envptr^ [envcnt]^ := envvar;
  688.       envcnt := envcnt + 1;
  689.       END;
  690.    END;
  691.  
  692.  
  693.  
  694. { Routines to search for files }
  695.  
  696. FUNCTION tryext (VAR fn : STRING) : INTEGER;
  697.  
  698.    { Try '.COM', '.EXE', and '.BAT' on current filename, modify filename if found. }
  699.  
  700.    VAR
  701.       nfn : filename;
  702.       ok : BOOLEAN;
  703.    BEGIN
  704.    tryext := 1;
  705.    nfn := fn + '.COM';
  706.    ok := exists (nfn);
  707.    IF NOT ok
  708.       THEN BEGIN
  709.       nfn := fn + '.EXE';
  710.       ok := exists (nfn);
  711.       END;
  712.    IF NOT ok
  713.       THEN BEGIN
  714.       tryext := 2;
  715.       nfn := fn + '.BAT';
  716.       ok := exists (nfn);
  717.       END;
  718.    IF NOT ok
  719.       THEN tryext := 0
  720.       ELSE fn := nfn;
  721.    END;
  722.  
  723.  
  724. FUNCTION findfile (VAR fn : STRING) : INTEGER;
  725.  
  726.    { Try to find the file 'fn' in the current path. Modifies the filename
  727.      accordingly. }
  728.  
  729.    VAR
  730.       path : STRING;
  731.       i, j : INTEGER;
  732.       hasext, found, check : INTEGER;
  733.    BEGIN
  734.    IF fn = ''
  735.       THEN BEGIN
  736.       IF cmdpath = ''
  737.          THEN findfile := ERR_COMSPEC
  738.          ELSE findfile := 3;
  739.       EXIT;
  740.       END;
  741.  
  742.    check := checkpath (fn, INF_NODIR, drive, dir, name, ext, fn);
  743.    IF check < 0
  744.       THEN BEGIN
  745.       findfile := check;
  746.       EXIT;
  747.       END;
  748.  
  749.    IF ((check AND HAS_WILD) <> 0) OR ((check AND HAS_FNAME) = 0)
  750.       THEN BEGIN
  751.       findfile := ERR_FNAME;
  752.       EXIT;
  753.       END;
  754.  
  755.    IF (check AND HAS_EXT) <> 0
  756.       THEN BEGIN
  757.       FOR i := 1 TO LENGTH (ext) DO
  758.          ext [i] := UPCASE (ext [i]);
  759.       IF ext = '.BAT'
  760.          THEN hasext := 2
  761.          ELSE hasext := 1;
  762.       END
  763.       ELSE hasext := 0;
  764.  
  765.    IF hasext <> 0
  766.       THEN BEGIN
  767.       IF (check AND FILE_EXISTS) <> 0
  768.          THEN found := hasext
  769.          ELSE found := 0;
  770.       END
  771.       ELSE found := tryext (fn);
  772.  
  773.    IF (found <> 0) OR ((check AND (HAS_PATH OR HAS_DRIVE)) <> 0)
  774.       THEN BEGIN
  775.       findfile := found;
  776.       EXIT;
  777.       END;
  778.  
  779.    path := getenv ('PATH');
  780.    i := 1;
  781.    WHILE (found = 0) AND (i <= LENGTH (path)) DO
  782.       BEGIN
  783.       j := 0;
  784.       WHILE (path [i] <> ';') AND (i <= LENGTH (path)) DO
  785.          BEGIN
  786.          j := j + 1;
  787.          fn [j] := path [i];
  788.          i := i + 1;
  789.          END;
  790.       i := i + 1;
  791.       IF (j > 0)
  792.          THEN BEGIN
  793.          IF NOT (fn [j] IN ['\', '/'])
  794.             THEN BEGIN
  795.             j := j + 1;
  796.             fn [j] := '\';
  797.             END;
  798.          fn [0] := CHR (j);
  799.          fn := fn + name + ext;
  800.          check := checkpath (fn, INF_NODIR, drive, dir, name, ext, fn);
  801.          IF hasext <> 0
  802.             THEN BEGIN
  803.             IF (check AND FILE_EXISTS) <> 0
  804.                THEN found := hasext
  805.                ELSE found := 0;
  806.             END
  807.             ELSE found := tryext (fn);
  808.          END;
  809.       END;
  810.    findfile := found;
  811.    END; { findfile }
  812.  
  813.  
  814. {>e 
  815.    Get name and path of the command processor via the COMSPEC
  816.    environmnt variable. Any parameters after the program name
  817.    are copied and inserted into the command line.
  818. <}
  819. {>d
  820.    Namen und Pfad des Kommandoprozessors über die COMSPEC-Umgebungs-
  821.    Variable bestimmen. Parameter nach dem Programmnamen werden kopiert
  822.    und in die Kommandozeile eingefügt.
  823. <}
  824.  
  825. PROCEDURE getcmdpath;
  826.    VAR
  827.       i, found : INTEGER;
  828.    BEGIN
  829.    IF LENGTH (cmdpath) > 0
  830.       THEN EXIT;
  831.    cmdpath := getenv ('COMSPEC');
  832.    cmdpars := '';
  833.    found := 0;
  834.  
  835.    IF cmdpath <> ''
  836.       THEN BEGIN
  837.       i := 1;
  838.       WHILE (i <= LENGTH (cmdpath)) AND (cmdpath [i] IN spaces) DO
  839.          INC (i);
  840.       IF i > 1
  841.          THEN BEGIN
  842.          cmdpath := COPY (cmdpath, i, 255);
  843.          i := 1;
  844.          END;
  845.  
  846.       i := strpbrk (cmdpath, ';,=+/"[]|<> '#9);
  847.       IF i <> 0
  848.          THEN BEGIN
  849.          cmdpars := COPY (cmdpath, i, 128);
  850.          cmdpath [0] := CHR (i - 1);
  851.          i := 1;
  852.          WHILE (i <= LENGTH (cmdpars)) AND (cmdpars [i] IN spaces) DO
  853.             INC (i);
  854.          IF i > 1
  855.             THEN cmdpars := COPY (cmdpars, i, 128);
  856.          IF cmdpars <> ''
  857.             THEN cmdpars := cmdpars + ' ';
  858.          END;
  859.       found := findfile (cmdpath);
  860.       END;
  861.  
  862.    IF found = 0
  863.       THEN BEGIN
  864.       cmdpath := 'COMMAND.COM';
  865.       cmdpars := '';
  866.       found := findfile (cmdpath);
  867.       IF found = 0
  868.          THEN cmdpath := '';
  869.       END;
  870.    END;
  871.  
  872.  
  873. FUNCTION tempdir (VAR outfn : filename) : BOOLEAN;
  874.  
  875.    { Set temporary file path.
  876.      Read "TMP/TEMP" environment. If empty or invalid, clear path.
  877.      If TEMP is drive or drive+backslash only, return TEMP.
  878.      Otherwise check if given path is a valid directory.
  879.    }
  880.    VAR
  881.       stmp : ARRAY [0..3] OF filename;
  882.       i, res : INTEGER;
  883.  
  884.    BEGIN
  885.    stmp [0] := getenv ('TMP');
  886.    stmp [1] := getenv ('TEMP');
  887.    stmp [2] := '.\';
  888.    stmp [3] := '\';
  889.  
  890.    FOR i := 0 TO 3 DO
  891.       IF LENGTH (stmp [i]) <> 0
  892.          THEN BEGIN
  893.          outfn := stmp [i];
  894.          res := checkpath (outfn, 0, drive, dir, name, ext, outfn);
  895.          IF (res > 0) AND ((res AND IS_DIR) <> 0) AND ((res AND IS_READ_ONLY) = 0)
  896.             THEN BEGIN
  897.             tempdir := TRUE;
  898.             EXIT;
  899.             END;
  900.          END;
  901.    tempdir := FALSE;
  902.    END;
  903.  
  904.  
  905. {$IFDEF REDIRECT}
  906.  
  907. FUNCTION parse_redirect (VAR par : STRING; idx : INTEGER;
  908.                          VAR stdin, stdout, stderr : pstring) : BOOLEAN;
  909.    VAR
  910.       ch : CHAR;
  911.       fnp : pstring;
  912.       fn : STRING;
  913.       app, i, beg, fne : INTEGER;
  914.  
  915.    BEGIN
  916.    i := idx;
  917.    par [LENGTH (par) + 1] := #0;
  918.  
  919.    REPEAT
  920.       app := 0;
  921.       ch := par [i];
  922.       beg := i;
  923.       i := i + 1;
  924.       IF ch <> '<'
  925.          THEN BEGIN
  926.          IF par [i] = '&'
  927.             THEN BEGIN
  928.             ch := '&';
  929.             INC (i);
  930.             END;
  931.          IF par [i] = '>'
  932.             THEN BEGIN
  933.             app := 1;
  934.             INC (i);
  935.             END;
  936.          END;
  937.  
  938.       WHILE (i <= LENGTH (par)) AND (par [i] IN spaces) DO
  939.          INC (i);
  940.       fn := COPY (par, i, 255);
  941.       fne := strpbrk (fn, ';,=+/"[]|<> '#9);
  942.       IF fne = 0
  943.          THEN fne := LENGTH (fn) + 1;
  944.       par := COPY (par, 1, beg - 1) + COPY (fn, fne, 255);
  945.       i := beg;
  946.       fn [0] := CHR (fne - 1);
  947.       IF (fne = 0) OR (LENGTH (fn) = 0)
  948.          THEN BEGIN
  949.          parse_redirect := FALSE;
  950.          EXIT;
  951.          END;
  952.       
  953.       GETMEM (fnp, LENGTH (fn) + app + 2);
  954.       IF fnp = NIL
  955.          THEN BEGIN
  956.          parse_redirect := FALSE;
  957.          EXIT;
  958.          END;
  959.       IF app <> 0
  960.          THEN fnp^ := '>' + fn
  961.          ELSE fnp^ := fn;
  962.       fnp^ [LENGTH (fnp^) + 1] := #0;
  963.  
  964.       CASE ch OF
  965.          '<' :  IF stdin <> NIL
  966.                   THEN BEGIN
  967.                   parse_redirect := FALSE;
  968.                   EXIT;
  969.                   END
  970.                ELSE stdin := fnp;
  971.  
  972.          '>' :  IF stdout <> NIL
  973.                   THEN BEGIN
  974.                   parse_redirect := FALSE;
  975.                   EXIT;
  976.                   END
  977.                ELSE stdout := fnp;
  978.  
  979.          '&' :  IF stderr <> NIL
  980.                   THEN BEGIN
  981.                   parse_redirect := FALSE;
  982.                   EXIT;
  983.                   END
  984.                ELSE stderr := fnp;
  985.          END;
  986.  
  987.       i := strpbrk (fn, '<>');
  988.    UNTIL (i <= 0);
  989.  
  990.    par [LENGTH (par) + 1] := #0;
  991.    parse_redirect := TRUE;
  992.    END;
  993.  
  994. {$ENDIF}
  995.  
  996.  
  997. FUNCTION do_exec (xfn : STRING; pars : STRING; spawn : INTEGER;
  998.                   needed : WORD; newenv : BOOLEAN) : INTEGER;
  999.    LABEL
  1000.       EXIT;
  1001.    VAR
  1002.       cmdbat : INTEGER;
  1003.       swapfn : filename;
  1004.       avail : WORD;
  1005.       regs : REGISTERS;
  1006.       envlen, einx : WORD;
  1007.       idx, len, rc : INTEGER;
  1008.       envp : bytearrayptr;
  1009.       swapping : INTEGER;
  1010. {$IFDEF REDIRECT}
  1011.       stdin, stdout, stderr : pstring;
  1012. {$ENDIF}
  1013.    BEGIN
  1014. {$IFDEF REDIRECT}
  1015.    stdin := NIL; stdout := NIL; stderr := NIL;
  1016. {$ENDIF}
  1017.  
  1018.    getcmdpath;
  1019.    envlen := 0;
  1020.  
  1021.    { First, check if the file to execute exists. }
  1022.  
  1023.    cmdbat := findfile (xfn);
  1024.    IF cmdbat <= 0
  1025.       THEN BEGIN
  1026.       do_exec := RC_NOFILE OR - cmdbat;
  1027.       GOTO EXIT;
  1028.       END;
  1029.  
  1030.    IF cmdbat > 1   { COMMAND.COM or Batch file }
  1031.       THEN BEGIN
  1032.       IF LENGTH (cmdpath) = 0
  1033.          THEN BEGIN
  1034.          do_exec := RC_NOFILE OR - ERR_COMSPEC;
  1035.          GOTO EXIT;
  1036.          END;
  1037.  
  1038.       IF cmdbat = 2
  1039.          THEN pars := cmdpars + '/c ' + xfn + ' ' + pars
  1040.          ELSE pars := cmdpars + pars;
  1041.       xfn := cmdpath;
  1042.       END;
  1043.  
  1044. {$IFDEF REDIRECT}
  1045.    idx := strpbrk (pars, '<>');
  1046.    IF idx > 0
  1047.       THEN IF NOT parse_redirect (pars, idx, stdin, stdout, stderr)
  1048.          THEN BEGIN
  1049.          do_exec := RC_REDIRERR;
  1050.          GOTO EXIT;
  1051.          END;
  1052. {$ENDIF}
  1053.  
  1054.    { Now create a copy of the environment if the user wants it, and
  1055.      if the environment has been changed. }
  1056.  
  1057.    IF newenv AND (envptr <> NIL)
  1058.       THEN BEGIN
  1059.       FOR idx := 0 TO envcnt - 1 DO
  1060.          envlen := envlen + LENGTH (envptr^ [idx]^) + 1;
  1061.       IF envlen > 0
  1062.          THEN BEGIN
  1063.          envlen := envlen + 1;
  1064.          GETMEM (envp, envlen);
  1065.          IF envp = NIL
  1066.             THEN BEGIN
  1067.             do_exec := RC_ENVERR;
  1068.             GOTO EXIT;
  1069.             END;
  1070.          einx := 0;
  1071.          FOR idx := 0 TO envcnt - 1 DO
  1072.             BEGIN
  1073.             len := LENGTH (envptr^ [idx]^);
  1074.             MOVE (envptr^ [idx]^ [1], envp^ [einx], len);
  1075.             envp^ [einx + len] := 0;
  1076.             einx := einx + len + 1;
  1077.             END;
  1078.          envp^ [einx] := 0;
  1079.          END;
  1080.       END;
  1081.  
  1082.    IF spawn = 0
  1083.       THEN swapping := - 1
  1084.       ELSE BEGIN
  1085.  
  1086.       { Determine amount of free memory }
  1087.       WITH regs DO
  1088.          BEGIN
  1089.          ax := $4800;
  1090.          bx := $ffff;
  1091.          MSDOS (regs);
  1092.          avail := regs.bx;
  1093.          END;
  1094.  
  1095.       { No swapping if available memory > needed }
  1096.  
  1097.       IF needed < avail
  1098.          THEN swapping := 0
  1099.          ELSE BEGIN
  1100.  
  1101.          { Swapping necessary, use 'TMP' or 'TEMP' environment variable
  1102.            to determine swap file path if defined. }
  1103.  
  1104.          swapping := spawn;
  1105.          IF (spawn AND USE_FILE) <> 0
  1106.             THEN BEGIN
  1107.             IF NOT tempdir (swapfn)
  1108.                THEN BEGIN
  1109.                spawn := spawn XOR USE_FILE;
  1110.                swapping := spawn;
  1111.                END
  1112.                ELSE BEGIN
  1113.                IF (dosversion AND $ff) >= 3
  1114.                   THEN swapping := swapping OR CREAT_TEMP
  1115.                   ELSE BEGIN
  1116.                   swapfn := swapfn + swap_filename;
  1117.                   len := LENGTH (swapfn);
  1118.                   WHILE exists (swapfn) DO
  1119.                      BEGIN
  1120.                       IF (swapfn [len] >= 'Z')
  1121.                         THEN len := len - 1;
  1122.                       IF (swapfn [len] = '.')
  1123.                         THEN len := len - 1;
  1124.                       swapfn [len] := SUCC (swapfn [len]);
  1125.                       END;
  1126.                   END;
  1127.                swapfn [LENGTH (swapfn) + 1] := #0;
  1128.                END;
  1129.             END;
  1130.          END;
  1131.       END;
  1132.  
  1133.    { All set up, ready to go. }
  1134.  
  1135.    IF swapping > 0
  1136.       THEN BEGIN
  1137.       IF envlen = 0
  1138.          THEN swapping := swapping OR DONT_SWAP_ENV;
  1139.  
  1140.       rc := prep_swap (swapping, swapfn);
  1141.       IF rc < 0
  1142.          THEN BEGIN
  1143.          do_exec := RC_PREPERR OR - rc;
  1144.          GOTO EXIT;
  1145.          END;
  1146.       END;
  1147.  
  1148.    xfn [LENGTH (xfn) + 1] := #0;
  1149.    pars [LENGTH (pars) + 1] := #0;
  1150.  
  1151.    IF @spawn_check <> NIL
  1152.       THEN BEGIN
  1153.       rc := spawn_check (cmdbat, swapping, xfn, pars);
  1154.       IF rc <> 0
  1155.          THEN BEGIN
  1156.          do_exec := rc;
  1157.          GOTO EXIT;
  1158.          END;
  1159.       END;
  1160.  
  1161.    swapvectors;
  1162. {$IFDEF REDIRECT}
  1163.    do_exec := do_spawn (swapping, xfn, pars, envlen, envp^, stdin, stdout, stderr);
  1164. {$ELSE}
  1165.    do_exec := do_spawn (swapping, xfn, pars, envlen, envp^);
  1166. {$ENDIF}
  1167.    swapvectors;
  1168.  
  1169.    { Free the environment buffer if it was allocated. }
  1170.  
  1171. EXIT :
  1172.    IF envlen > 0
  1173.       THEN FREEMEM (envp, envlen);
  1174. {$IFDEF REDIRECT}
  1175.    IF stdin <> NIL
  1176.       THEN FREEMEM (stdin, LENGTH (stdin^) + 2);
  1177.    IF stdout <> NIL
  1178.       THEN FREEMEM (stdout, LENGTH (stdout^) + 2);
  1179.    IF stderr <> NIL
  1180.       THEN FREEMEM (stderr, LENGTH (stderr^) + 2);
  1181. {$ENDIF}
  1182.    END;
  1183.  
  1184.  
  1185. { Initialisation for environment processing }
  1186.  
  1187. BEGIN
  1188. envptr := NIL;
  1189. envcnt := 0;
  1190. cmdpath := '';
  1191. @spawn_check := NIL;
  1192. END.
  1193.